package com.demiroot.server;

import java.io.File;
import java.io.FileInputStream;
import java.io.FilenameFilter;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.net.URISyntaxException;
import java.net.URL;
import java.net.URLDecoder;
import java.security.MessageDigest;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Random;
import java.util.Scanner;
import java.util.TimeZone;
import java.util.regex.Pattern;

import com.demiroot.server.HeaderData.HeaderField;
import com.demiroot.server.pages.Page;


public class ServerUtils {

	private static Map<Integer, String> statusMessage = new HashMap<Integer, String>();
	private static MessageDigest md5 = null;
	private static final char[] hexChars ={'0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F'}; 
	
	public static void load() throws URISyntaxException {
		if (statusMessage.size() != 0) return;
		
		statusMessage.put(new Integer(200), "OK");
		statusMessage.put(new Integer(404), "File Not Found");
		statusMessage.put(new Integer(500), "Server Error");
	}

	/**
     * Returns the byte array of a file
     * @param file The file to red
     * @return byte[] the file in array form
     */
    private static byte[] getBytesFromFile(File file) throws IOException {
        InputStream is = new FileInputStream(file);
        int length;
        try {
        	length = (int)file.length();
        } catch (Exception e) {
        	System.err.println("Error file to big: " + file.getName());
        	return null;
        }
        byte[] bytes = new byte[(int)length];
        
        int left = length;
        while (left > 0) {
        	left -= is.read(bytes,length - left, left);
        }

        is.close();
        return bytes;
    }
    
	public static byte[] stringToByteArray(String message) {
		byte[] ret = new byte[message.length()];
		for (int x = 0;x < message.length(); x++) {
			ret[x] = (byte)message.charAt(x);
		}
		return ret;
	}
	
	public static byte[] fileToByteArray(URL file) throws IOException, URISyntaxException {
		if (file.getPath().endsWith("html") 
				|| file.getPath().endsWith("htm")
				|| file.getPath().endsWith("txt")) {
			Scanner s = new Scanner(file.openStream());
			String out = "";
			while(s.hasNext()) {
				out += s.nextLine() + "\n";
			}
			out += Server.ENDL;
			s.close();
			return ServerUtils.stringToByteArray(out);
		}
		
		return ServerUtils.getBytesFromFile(new File(file.toURI()));
	}	
	
	public static String getHeaderString(HTTPResponse message, int status) {
		String head = "HTTP/1.1 " + status + " " 
			+ statusMessage.get(new Integer(status)) + Server.ENDL;
		
		HeaderData header = message.getHeader();
		if (header == null) {
			header = new HeaderData();
		}
		
		if (header.get(HeaderField.CONTENT_LENGTH) == null) {
			header.set(HeaderField.CONTENT_LENGTH, "" +  message.getMessageAsByteArray().length);
		}
		
		//generate cookies
		List<Cookie> cookies = message.getCookies();
		if (cookies != null && cookies.size() != 0) {
			head += "Set-Cookie";
			for (Cookie c : cookies) {
				head += ":" + c.getKey() + "=" + c.getValue();				
			}
			if (message.cookieExpire() != null) {
				head += ";expires=" + getWebDate(message.cookieExpire());
			}
			head += Server.ENDL;
		}
		head += header.toString();
		head += Server.ENDL;
		
		Logger.log("headers", head);
		
		return head;
	}
	
	public static byte[] getHeader(HTTPResponse message, int status) {
		return stringToByteArray(getHeaderString(message, status));
	}
	
	public static void sendResponse(OutputStream os, HTTPResponse message) throws IOException, InterruptedException {
		sendHeaderResponse(os, message, message.getCode());
		write(os,message.getMessageAsByteArray());
	}

	public static void sendHeaderResponse(OutputStream os, HTTPResponse message, int status) throws IOException, InterruptedException {
		write(os, getHeader(message, status));
	}
	
	public static void write(OutputStream os, byte[] message) throws InterruptedException, IOException {
		os.write(message);
		os.flush();
		//this is needed or firefox will only load a page some of the time
		Thread.sleep(1);
	}
	
	public static Map<String, String> seperateContent(byte[] con) throws UnsupportedEncodingException {
		Map<String, String> ret = new HashMap<String, String>();
		
		String content = "";
		for (byte c : con) {
			content += (char)c;
		}
		
		Scanner s = new Scanner(content);
		s.useDelimiter(Pattern.compile("=|&"));

		while (s.hasNext()) {
			ret.put(URLDecoder.decode(s.hasNext() ? s.next() : "", "utf-8").trim(),URLDecoder.decode(s.hasNext() ? s.next() : "","utf-8").trim());
		}
		return ret;
	}

	public static String generateFileFromTemplate(String file,
			Map<String, String> swap, Config config) {
		
		try {
			URL url = null;
			for (Config.Folder tFolders : config.getTemplateLocs()) {
				url = ClassLoader.getSystemResource(tFolders.getLocation() + "/" + file);
				if (url != null) break;
			}
			if (url == null) {
				throw new RuntimeException("Error could not create template: " + file);
			}
			Scanner s = new Scanner(new File(url.toURI()));
			
			String ret = "";
			String line;
			Scanner s2;
			String word;
			while (s.hasNext()) {
				line=s.nextLine();
				if (line.indexOf("$") != -1) {
					s2 = new Scanner(line);
					while (s2.hasNext()) {
						word = s2.next();
						if (word.startsWith("$") && swap.containsKey(word)) {
							word = swap.get(word);
						}
						ret += word + " ";
					}
					s2.close();
				} else {
					ret += line;
				}
				ret += "\n";
			}
			s.close();
			return ret;
		} catch (Exception e) {
			throw new RuntimeException(e);
		}
	}
	
	public static String getWebDate(Calendar cal) {
		String date = "";
		SimpleDateFormat sdf = new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss");
		sdf.setTimeZone(TimeZone.getTimeZone("GMT"));
		date += sdf.format(cal.getTime());
		date += " GMT";
		return date;
	}
	
	public static String md5(String message) {
		try {
			if (md5 == null) {
				md5 = MessageDigest.getInstance("MD5");
			}
			md5.update(message.getBytes(), 0, message.getBytes().length);
			byte[] bhash = md5.digest();
			return hexStringFromBytes(bhash).toLowerCase();
		} catch (Exception e) {
			throw new RuntimeException(e);
		}
	}
	
	//borrowed from http://www.osix.net/modules/article/?id=42
	public static String hexStringFromBytes(byte[] b) {
		String hex = "";
		int msb;
		int lsb = 0;
		int i;

		for (i = 0; i < b.length; i++) {

			msb = ((int)b[i] & 0x000000FF) / 16;
			lsb = ((int)b[i] & 0x000000FF) % 16;
			hex = hex + hexChars[msb] + hexChars[lsb];
		}
		return(hex);
	} 
	
	public static String randomMD5() {
		String ret = "";
		Random rand = new Random();
		for (int x = 0; x < 128; x++) {
			ret += (char)rand.nextInt(255);
		}
		return ServerUtils.md5(ret);	
	}
	
	public static String getHeaderData(InputStream is) throws IOException {
		StringBuffer sb = new StringBuffer();
		while (!sb.toString().endsWith(Server.ENDL + Server.ENDL)) {
			int b = is.read();
			sb.append((char)b);
		}
		Logger.log("request", sb.toString());
		return sb.toString();
	}
}
